package cn.cosmosx.base.util;

import cn.cosmosx.base.dict.IDict;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import cn.cosmosx.base.constant.ResultCode;
import cn.cosmosx.base.exception.BusinessException;
import cn.cosmosx.base.exception.PlatformException;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Closeable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public abstract class CommonUtils {

    private final static String METHOD_GET = "get", METHOD_IS = "is", BASE_TYPE_STRING = "String", BASE_TYPE_BOOLEAN = "boolean",
            BASE_TYPE_INTEGER = "Integer", BASE_TYPE_INT = "int";

    /**
     * 获取去下划线的uuid
     *
     * @return
     */
    public static String getUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    /**
     * map 转换为实例
     *
     * @param map
     * @param classType
     * @param <T>
     * @return
     */
    public static <T> T mapToInstance(Map<String, Object> map, Class<T> classType) {
        T t;
        try {
            t = classType.newInstance();
        } catch (InstantiationException e) {
            throw new BusinessException(
                    String.format("[%s]类中缺少无参构造函数", classType.getSimpleName()));
        } catch (IllegalAccessException e) {
            throw new BusinessException(
                    String.format("[%s]类中缺少公开无参构造函数", classType.getSimpleName()));
        }
        if (map == null || map.size() == 0) {
            return t;
        }
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key = entry.getKey();
            Field field = ClassUtils.getField(classType, key);
            if (field == null) {
                continue;
            }
            field.setAccessible(true);
            Object value = entry.getValue();
            if (value == null) {
                continue;
            }
            if (value instanceof HashMap) {
                value = mapToInstance((Map<String, Object>) value, field.getType());
            } else if (value instanceof List) {
                Type gt = field.getGenericType();
                if (!(gt instanceof ParameterizedType)) {
                    continue;
                }
                ParameterizedType genericType = (ParameterizedType) field.getGenericType();
                Type genericClassType = genericType.getActualTypeArguments()[0];
                value = ((List<Map<String, Object>>) value)
                        .stream().map(x -> mapToInstance(x, (Class<?>) genericClassType))
                        .collect(Collectors.toList());
            } else if (value instanceof String) {
                String valueStr = (String) value;
                if (StringUtils.isBlank(valueStr)) {
                    continue;
                }
                value = getValueByString(valueStr, field.getType());
            }
            ClassUtils.setFieldValue(t, field, value);
        }
        return t;
    }

    /**
     * 把String 类型的值 转换为指定类型值
     *
     * @param value
     * @param type
     * @return
     */
    public static Object getValueByString(String value, Class type) {
        if (StringUtils.isBlank(value)) {
            return null;
        }
        if (Date.class.isAssignableFrom(type)) {
            String dateFormat;
            if (value.matches("^\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}$")) {
                dateFormat = "yyyy-MM-dd HH:mm:ss";
            } else {
                dateFormat = "yyyy-MM-dd";
            }
            try {
                return new SimpleDateFormat(dateFormat).parse(value);
            } catch (ParseException e) {
                throw new IllegalArgumentException(value + "无法转换为时间只支持[yyyy-MM-dd]和[yyyy-MM-dd HH:mm:ss]的格式");
            }
        } else if (Long.class.isAssignableFrom(type)) {
            return Long.valueOf(value);
        } else if (BigDecimal.class.isAssignableFrom(type)) {
            return new BigDecimal(value);
        } else if (IDict.class.isAssignableFrom(type)) {
            return IDict.findByValue(value, type);
        } else if (Enum.class.isAssignableFrom(type)) {
            //先处理字典再处理普通枚举
            return Enum.valueOf(type, value);
        }
        return value;
    }

    /**
     * 复制属性，只处理相同字段
     *
     * @param instances       原实例
     * @param resultClassType 新实例类型
     * @param <T>
     * @param <R>
     * @return
     */
    public static <T, R> List<R> copyProperty(List<T> instances, Class<R> resultClassType) {
        if (CollectionUtils.isEmpty(instances)) {
            return Collections.emptyList();
        }
        List<R> results = new ArrayList<>(instances.size());
        try {
            for (T instance : instances) {
                results.add(copyProperty(instance, resultClassType));
            }
        } catch (Exception e) {
            throw new PlatformException(ResultCode.E00002, e.getMessage());
        }
        return results;
    }


    public static <T, R> R copyProperty(T instance, Class<R> resultClassType) {
        if (instance == null || resultClassType == null) {
            return null;
        }
        R result;
        try {
            result = resultClassType.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            throw new PlatformException(ResultCode.E00002, e.getMessage());
        }
        return copyProperty(instance, result);
    }

    /**
     * 复制属性，只处理相同字段
     *
     * @param instance
     * @param resultInstance
     * @param <T>
     * @param <R>
     * @return
     */
    public static <T, R> R copyProperty(T instance, R resultInstance) {
        if (instance == null || resultInstance == null) {
            return null;
        }
        Class<R> resultClassType = (Class<R>) resultInstance.getClass();
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(instance.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            Object invoke;
            Field resultField;
            for (PropertyDescriptor p : propertyDescriptors) {
                Method readMethod = p.getReadMethod();
                if (readMethod == null || null == p.getWriteMethod()
                        || (invoke = readMethod.invoke(instance)) == null) {
                    continue;
                }
                try {
                    resultField = ClassUtils.getField(resultClassType, p.getName());
                } catch (PlatformException e) {
                    continue;
                }
                enumHandle(resultInstance, resultClassType, resultField, invoke);
            }
        } catch (Exception e) {
            throw new PlatformException(ResultCode.E00002, e.getMessage());
        }
        return resultInstance;
    }


    /**
     * 处理枚举(只有当字段一致，但值类型不一致的枚举和基本类型才会转换)
     *
     * @param field    字段
     * @param param    原始对象返回的数据
     * @param instance 需要塞入值得对象
     * @param <T>
     * @return 枚举转换成功返回true
     */
    public static <T> void enumHandle(T instance, Class<T> resultClassType, Field field, Object param) {
        try {
            if (null == param) {
                return;
            }
            // 参数是枚举
            Method setMethod = ClassUtils.getMethod(resultClassType, "set" + StringUtils.capitalize(field.getName()));
            if (IDict.class.isAssignableFrom(param.getClass()) && isBaseTypeOrEnum(field.getType())) {
                setMethod.invoke(instance, param);
                return;
            }
            if (IDict.class.isAssignableFrom(param.getClass())) {
                param = ((IDict) param).getDesc();
            } else if (isBaseTypeOrEnum(field.getType())) {
                Class fieldType = field.getType();
                param = IDict.findByValue(param.toString(), fieldType);
            } else {
                if (param.getClass().getSimpleName().equalsIgnoreCase(BASE_TYPE_BOOLEAN)) {
                    if (field.getType().getSimpleName().equalsIgnoreCase(BASE_TYPE_INT) || field.getType().getSimpleName().equalsIgnoreCase(BASE_TYPE_INTEGER)) {
                        param = Boolean.getBoolean(param.toString()) ? 1 : 0;
                    } else if (field.getType().getSimpleName().equalsIgnoreCase(BASE_TYPE_STRING)) {
                        param = Boolean.getBoolean(param.toString()) ? "1" : "0";
                    }
                }
                if (param.getClass().getSimpleName().equalsIgnoreCase(BASE_TYPE_INT) || param.getClass().getSimpleName().equalsIgnoreCase(BASE_TYPE_INTEGER) || param.getClass().getSimpleName().equalsIgnoreCase(BASE_TYPE_STRING)) {
                    if (field.getType().getSimpleName().equalsIgnoreCase(BASE_TYPE_BOOLEAN)) {
                        param = "1".equals(param.toString());
                    }
                }
            }
            setMethod.invoke(instance, param);
        } catch (Exception ignored) {
        }
    }

    /**
     * 判断一个类是否是枚举 String或int类型
     *
     * @param clazz 目标类
     * @return
     */
    private static boolean isBaseTypeOrEnum(Class clazz) {
        return IDict.class.isAssignableFrom(clazz);
    }


    /**
     * list 转换
     *
     * @param values 数据
     * @param call   回调函数
     * @param <R>
     * @param <I>
     * @return
     */
    public static <R, I> List<R> transform(List<I> values, Function<I, R> call) {
        if (CollectionUtils.isEmpty(values)) {
            return Collections.emptyList();
        }
        List<R> result = new ArrayList<>(values.size());
        values.forEach(value -> result.add(call.apply(value)));
        return result;
    }

    /**
     * 关闭流
     *
     * @param streams
     */
    @SneakyThrows
    public static void closeStream(Object... streams) {
        for (Object value : streams) {
            if (value == null) {
                continue;
            }
            if (value instanceof Closeable) {
                Closeable closeable = (Closeable) value;
                closeable.close();
            } else {
                try {
                    Method close = value.getClass().getMethod("close");
                    close.invoke(value);
                } catch (NoSuchMethodException e) {
                    throw new BusinessException("[%s]类不包含Close方法", value.getClass().getSimpleName());
                }
            }
        }
    }
}
